package com.sfzd5.exam.helplibrary;

import org.bytedeco.javacpp.opencv_core.*;
import org.bytedeco.javacpp.opencv_imgproc;

import static org.bytedeco.javacpp.opencv_core.*;
import static org.bytedeco.javacpp.opencv_imgproc.*;

import java.util.Properties;


/**
 *
 *
 AndroidFrameConverter
 Java2DFrameConverter
 JavaFXFrameConverter
 LeptonicaFrameConverter
 OpenCVFrameConverter

 *
 *         org.bytedeco.opencv.global.opencv_imgproc.rectangle(image, new Point(0, 0), new Point(1000, 1000),
 *                 new Scalar(255,255,255,0),
 *                 -1,
 *                 LINE_8,
 *                 0);
 *         //image.zero();
 *         org.bytedeco.opencv.global.opencv_imgproc.rectangle(image, new Point(10, 10), new Point(300, 300), Scalar.ALPHA255, 5, LINE_8, 0);
 *         line(image, new Point(0,0), new Point(500,500), Scalar.ALPHA255);
 *         if (image != null) {
 *             //GaussianBlur(image, image, new Size(3, 3), 0);
 *             imwrite("t3.png", image);
 *         }
 */
public class DrawMatHelper {

    private static WindowsDrawStringEventListener windowsDrawStringEventListener;
    public static void setWindowsDrawStringEventListener(WindowsDrawStringEventListener listener){
        windowsDrawStringEventListener = listener;
    }

    private boolean isWindows;

    public Mat getMat() {
        return mat;
    }

    private Mat mat;

    public DrawMatHelper(int width, int height){
        this.mat = new Mat(height, width, CV_8UC1);
        Properties props=System.getProperties(); //获得系统属性集
        String osName = props.getProperty("os.name").toLowerCase(); //操作系统名称
        isWindows = osName.contains("windows");
        mat.put(Scalar.WHITE);
    }

    public static void d(){
        Mat mat2 = new Mat(100, 100, CV_8UC1);
        Mat mat1 = new Mat(100, 100, CV_8UC1);

        //的有数据设置为0
        mat1.zero();

        mat1.put(mat2);

        //相当于fill
        mat2.put(new Scalar(0,0,0,0));

        //这相结合put就可以填充图片了
        Mat mat3 = mat1.apply(new Rect(0,0,10, 10));


        flip(mat1, mat2, 1);

        /**
         opencv提供的一些绘图函数，来简单的绘制一个简易的图形，包括线段、矩形、椭圆、多边形等。
         1.线段

         opencv提供了line()函数来对直线的绘制。其原型如下：

         CV_EXPORTS_W void line(CV_IN_OUT Mat& img, Point pt1, Point pt2, const Scalar& color,
         int thickness=1, int lineType=8, int shift=0);

         img: 要绘制线段的图像。
         pt1: 线段的起点。
         pt2: 线段的终点。
         color: 线段的颜色，通过一个Scalar对象定义。
         thickness: 线条的宽度。
         lineType: 线段的类型。可以取值8， 4， 和CV_AA， 分别代表8邻接连接线，4邻接连接线和反锯齿连接线。默认值为8邻接。为了获得更好地效果可以选用CV_AA(采用了高斯滤波)。
         shift: 坐标点小数点位数。
         2.椭圆

         opencv提供了ellipse()函数来绘制椭圆。其原型如下：

         CV_EXPORTS_W void ellipse(CV_IN_OUT Mat& img, Point center, Size axes,double angle, double startAngle, double endAngle,
         const Scalar& color, int thickness=1,int lineType=8, int shift=0);

         img: 要绘制椭圆的图像。
         center: 椭圆中心点坐标。
         axes: 椭圆位于该Size决定的矩形内。（即定义长轴和短轴）。
         angle: 椭圆旋转角度。
         startAngle: 椭圆开始绘制时角度。
         endAngle: 椭圆绘制结束时角度。（若绘制一个完整的椭圆，则startAngle=0, endAngle = 360）。
         color: 椭圆的颜色。
         thickness: 绘制椭圆线粗。负数表示全部填充。
         lineType，shift：同上。
         3.矩形

         opencv提供了rectangle()函数来绘制矩形。其原型如下：

         CV_EXPORTS_W void rectangle(CV_IN_OUT Mat& img, Point pt1, Point pt2,
         const Scalar& color, int thickness=1,int lineType=8, int shift=0);

         pt1: 矩形的左上角坐标。
         pt2: 矩阵的右下角坐标。
         其余同上。
         4.圆

         opencv提供了circle()函数来绘制圆。其原型如下：

         CV_EXPORTS_W void circle(CV_IN_OUT Mat& img, Point center, int radius,
         const Scalar& color, int thickness=1,int lineType=8, int shift=0);

         center: 圆心坐标。
         radius: 半径。
         其余同上。

         5.填充多边形

         opencv为我们提供了fillPoly()函数来绘制填充多边形。其原型如下：

         CV_EXPORTS void fillPoly(Mat& img, const Point** pts,const int* npts, int ncontours,
         const Scalar& color, int lineType=8, int shift=0,Point offset=Point() );

         pts: 多边形定点集。
         npts: 多边形的顶点数目。
         ncontours: 要绘制多边形的数量。
         offset: 所有点轮廓的可选偏移。
         其余同上。

         6.显示文字
         opencv提供和PutText（）函数来显示文字。其原型如下：

         void putText(Mat& img, const string& text, Point org, int fontFace, double fontScale, Scalar color,
         int thickness=1, int lineType=8, bool bottomLeftOrigin=false )

         img – 显示文字所在图像.
         text – 待显示的文字.
         org – 文字在图像中的左下角 坐标.
         font – 字体结构体.
         fontFace – 字体类型， 可选择字体：
         FONT_HERSHEY_SIMPLEX, FONT_HERSHEY_PLAIN,
         FONT_HERSHEY_DUPLEX, FONT_HERSHEY_COMPLEX,
         FONT_HERSHEY_TRIPLEX, FONT_HERSHEY_COMPLEX_SMALL,
         FONT_HERSHEY_SCRIPT_SIMPLEX, or FONT_HERSHEY_SCRIPT_COMPLEX,
         以上所有类型都可以配合 FONT_HERSHEY_ITALIC使用，产生斜体效果。
         fontScale – 字体大小，该值和字体内置大小相乘得到字体大小
         color – 文本颜色
         thickness – 写字的线的粗细
         lineType – 线型.
         bottomLeftOrigin – true, 图像数据原点在左下角. Otherwise, 图像数据原点在左上角.

         利用上述函数，实例1：

         #include<opencv2/core/core.hpp>
         #include<opencv2/highgui/highgui.hpp>

         using namespace cv;
         #define WINDOW_SIZE   400

         void DrawEllipse(Mat img,double angle);
         void DrawFilledCircle(Mat img,Point center);
         void DrawPolygon(Mat img);
         void DrawLine(Mat img,Point start,Point end);

         int main(int argc,char** argv)
         {
         //创建空白的图像
         Mat atomImg=Mat::zeros(WINDOW_SIZE,WINDOW_SIZE,CV_8UC3);
         Mat rookImg=Mat::zeros(WINDOW_SIZE,WINDOW_SIZE,CV_8UC3);

         //------------------------------------绘制原子的示例图------------------------------------
         //绘制出椭圆
         DrawEllipse(atomImg,90);
         DrawEllipse(atomImg,0);
         DrawEllipse(atomImg,45);
         DrawEllipse(atomImg,-45);
         namedWindow("ImageEll",CV_WINDOW_NORMAL);
         imshow("ImageEll",atomImg);
         //再绘制出圆心
         DrawFilledCircle(atomImg,Point(WINDOW_SIZE/2,WINDOW_SIZE/2));
         namedWindow("ImageEll+Circle",CV_WINDOW_NORMAL);
         imshow("ImageEll+Circle",atomImg);

         //绘制会多边形
         DrawPolygon(rookImg);
         //显示多边形
         namedWindow("polygon",CV_WINDOW_NORMAL);
         imshow("polygon",rookImg);
         //绘制矩形
         rectangle(rookImg,Point(0,7*WINDOW_SIZE/8),      //两个对角定点
         Point(WINDOW_SIZE,WINDOW_SIZE),
         Scalar(0,255,255),             //矩阵颜色图像
         -1,                            //由于线粗为-1,,此举真将被填充
         8);
         //显示多边形+矩形
         namedWindow("ImageRook",CV_WINDOW_NORMAL);
         imshow("ImageRook",rookImg);
         waitKey(0);
         return 0;
         }

         / ***************************************************************************************************
         函数功能:
         绘制一条直线
         函数参数:
         1---画一条从点start到点end的直线段
         2---此线段将被滑到图像img上
         3---线的颜色有Scalar(0,0,0)来定义
         4---线的粗细由thickness设定
         5---线的类型由lineType决定,此处为8连通
         *************************************************************************************************** /
        void DrawLine(Mat img,Point start,Point end)
        {
            int thickness=2;
            int lineType=8;
            line(img,start,end,Scalar(0,0,0),thickness,lineType);
        }
        / ***************************************************************************************************
         函数功能:
         绘制一个椭圆
         函数参数:
         1---椭圆将被画到img图像上
         2---椭圆的中心点为Point(WINDOW_SIZE/2.0,WINDOW_SIZE/2.0),并且大小位于矩阵
         Size(WINDOW_SIZE/4.0,WINDOW_SIZE/16.0),
         3---椭圆的旋转角度为angle
         4---椭圆拓展的弧度为0到360度
         5--图形颜色为Scalar(255,0,255)
         6--椭圆的粗度为thickness
         7--线条的类型为lineType---8连通域
         *************************************************************************************************** /
        void DrawEllipse(Mat img,double angle)
        {
            int thickness=2;
            int lineType=8;
            ellipse(img,
                    Point(WINDOW_SIZE/2.0,WINDOW_SIZE/2.0),
                    Size(WINDOW_SIZE/4.0,WINDOW_SIZE/16.0),
                    angle,
                    0,
                    360,
                    Scalar(255,0,255),
                    thickness,
                    lineType);
        }
        / ***************************************************************************************************
         函数功能:
         绘制一个圆
         函数参数:
         1---圆将被画到img图像上
         2---圆心由center定义
         3---圆的半径为:WINDOW_SIZE/32.0
         4---圆的颜色Scalar(0,0,255)
         5---线的粗细为thickness=-1,因此,圆将被填充
         *************************************************************************************************** /
        void DrawFilledCircle(Mat img,Point center)
        {
            int thickness=-1;
            int lineType=8;
            circle(img,
                    center,
                    WINDOW_SIZE/32.0,
                    Scalar(0,0,255),
                    thickness,
                    lineType);
        }
        / ***************************************************************************************************
         函数功能:
         绘制多边形
         函数参数:
         1---多边形将被画到img上
         2---多边形的顶点集为ppt
         3---要绘制的多边形定点数目为npt
         4---要绘制的多边形数量为1
         5---多边形的颜色定义为Scalar(255,255,255),即BGR的值为白色
         *************************************************************************************************** /
        void DrawPolygon(Mat img)
        {
            int lineType=0;

            //创建一些点
            Point rookPoints[1][20];
            rookPoints[0][0]  =Point(WINDOW_SIZE/4,7*WINDOW_SIZE/8);
            rookPoints[0][1]  =Point(3*WINDOW_SIZE/4,7*WINDOW_SIZE/8);
            rookPoints[0][2]  = Point(3*WINDOW_SIZE/4,13*WINDOW_SIZE/16 );
            rookPoints[0][3]  = Point(11*WINDOW_SIZE/16,13*WINDOW_SIZE/16 );
            rookPoints[0][4]  = Point(19*WINDOW_SIZE/32,3*WINDOW_SIZE/8 );
            rookPoints[0][5]  = Point(3*WINDOW_SIZE/4,3*WINDOW_SIZE/8 );
            rookPoints[0][6]  = Point(3*WINDOW_SIZE/4,WINDOW_SIZE/8 );
            rookPoints[0][7]  = Point(26*WINDOW_SIZE/40,WINDOW_SIZE/8 );
            rookPoints[0][8]  = Point(26*WINDOW_SIZE/40,WINDOW_SIZE/4 );
            rookPoints[0][9]  = Point(22*WINDOW_SIZE/40,WINDOW_SIZE/4 );
            rookPoints[0][10] = Point(22*WINDOW_SIZE/40,WINDOW_SIZE/8 );
            rookPoints[0][11] = Point(18*WINDOW_SIZE/40,WINDOW_SIZE/8 );
            rookPoints[0][12] = Point(18*WINDOW_SIZE/40,WINDOW_SIZE/4 );
            rookPoints[0][13] = Point(14*WINDOW_SIZE/40,WINDOW_SIZE/4 );
            rookPoints[0][14] = Point(14*WINDOW_SIZE/40,WINDOW_SIZE/8 );
            rookPoints[0][15] = Point(WINDOW_SIZE/4,WINDOW_SIZE/8 );
            rookPoints[0][16] = Point(WINDOW_SIZE/4,3*WINDOW_SIZE/8 );
            rookPoints[0][17] = Point(13*WINDOW_SIZE/32,3*WINDOW_SIZE/8 );
            rookPoints[0][18] = Point(5*WINDOW_SIZE/16,13*WINDOW_SIZE/16 );
            rookPoints[0][19] = Point(WINDOW_SIZE/4,13*WINDOW_SIZE/16 );

          const Point* ppt[1]={rookPoints[0]};
            int npt[]={20};
            fillPoly(img,
                    ppt,
                    npt,
                    1,
                    Scalar(255,255,255),
                    lineType);
        }
         */

        opencv_imgproc.line(
                mat1,
                new Point(0, 0),
                new Point(10, 10),
                Scalar.BLACK);

        //堆积
        opencv_imgproc.accumulate(mat1, mat2);

        //透明层
        //opencv_imgproc.adaptiveThreshold(mat1, mat1, );

        opencv_imgproc.boundingRect(mat1);

        opencv_imgproc.putText(mat1, "help mer", new Point(0,0), opencv_imgproc.FONT_HERSHEY_PLAIN, 0.15, Scalar.BLACK, 1, LINE_8, false);

        opencv_imgproc.rectangle(mat1, new Point(0,0), new Point(10, 10), Scalar.BLACK);

    }

    public void setStroke(float lineWidth) {
    }

    public void drawLine(int x1, int y1, int x2, int y2) {
        opencv_imgproc.line(mat, new Point(x1, y1), new Point(x2, y2), Scalar.BLACK);
    }

    public void drawRect(float x, float y, float w, float h, float lineWidth) {
        opencv_imgproc.rectangle(mat, new Point((int)x,(int)y), new Point((int)(x+w), (int)(y+h)), Scalar.BLACK);
    }

    public void drawRect(float x, float y, float w, float h) {
        drawRect(x, y, w, h, 1);
    }

    public void drawImage(Mat image, int x, int y, int w, int h) {
        int imgType = image.type();
        int matType = mat.type();
        if(image.type()!=mat.type()){
            cvtColor(image, image, CV_BGR2GRAY);
        }
        image.copyTo(mat.apply(new Rect(x, y, w, h)));
    }

    public void drawString(String str, float x, float y, float w, float h) {
        if(isWindows && windowsDrawStringEventListener != null){
            Mat strMat = windowsDrawStringEventListener.drawString(str, h);
            int mh = strMat.rows();
            int mw = strMat.cols();
            if(w!=-1){
                //居中放置
                x += (w - mw)/2;
            }
            drawImage(strMat, (int)x, (int)y, mw, mh);
        }
    }

    public void fillRect(int x, int y, int w, int h) {
        Mat fillMap = mat.apply(new Rect(x, y, w, h));
        fillMap.put(Scalar.BLACK);
    }
}
